package cn.ztuo.bitrade.util;

import cn.ztuo.bitrade.constant.SysConstant;
import cn.ztuo.bitrade.entity.*;
import cn.ztuo.bitrade.service.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import static cn.ztuo.bitrade.util.BigDecimalUtils.*;

@Slf4j
@Component
public class ContractUtils {

    @Autowired
    private RedisUtil redisUtilDouble;
    @Autowired
    private ContractBondService bondServiceDouble;
    @Autowired
    private ContractPositionService positionServiceDouble;
    @Autowired
    private ContractOrderService orderServiceDouble;
    @Autowired
    private ContractWalletService walletServiceDouble;
    @Autowired
    private ContractCoinService contractCoinServiceDouble;

    private static ContractBondService bondService;
    private static ContractPositionService positionService;
    private static ContractOrderService orderService;
    private static ContractWalletService walletService;
    private static ContractCoinService contractCoinService;
    private static RedisUtil redisUtil;

    public static Map<Long, ContractPosition> findAllPosition() {
        Map map = redisUtil.hmget(SysConstant.CONTRACT_POSITION);
        if (map == null || map.isEmpty()) {
            List<ContractPosition> list = positionService.findAll();
            map = list.stream().collect(Collectors.toMap(a -> a.getId().toString(), position -> position));
            redisUtil.hmset(SysConstant.CONTRACT_POSITION, map);
        }
        return map;
    }


    @PostConstruct
    public void init() {
        bondService = bondServiceDouble;
        positionService = positionServiceDouble;
        orderService = orderServiceDouble;
        redisUtil = redisUtilDouble;
        walletService = walletServiceDouble;
        contractCoinService = contractCoinServiceDouble;
    }

    /**
     * 利率(I) = (计价利率指数 – 基础利率指数)/资金费率间隔
     *
     * @param coinSymbolScale 计价货币的利率
     * @param baseSymbolScale 基础货币的利率
     * @return 利率
     */
    public static BigDecimal interestRate(String coinSymbolScale, String baseSymbolScale) {
        BigDecimal cssbigDecimal = new BigDecimal(coinSymbolScale);
        BigDecimal bssbigDecimal = new BigDecimal(baseSymbolScale);
        BigDecimal div = div(sub(cssbigDecimal, bssbigDecimal), new BigDecimal("3"));
        return div;
    }


    /**
     * 溢价指数(p) = (Max(0,深度加权买价-标记价格))-Max(0,标记价格-深度加权卖价)/现货价格+当前资金费率
     *
     * @param dwpp        深度加权买价
     * @param dwsp        深度加权卖价
     * @param markedprice 标记价格
     * @param spotprice   现货价格
     * @param capitalrate 当前资金费率
     * @return 溢价指数
     */
    public static BigDecimal premiumIndex(BigDecimal dwpp, BigDecimal dwsp, BigDecimal markedprice, BigDecimal spotprice, BigDecimal capitalrate) {
        BigDecimal add = add(div(sub(Math.max(0, sub(dwpp, markedprice).doubleValue()), Math.max(0, sub(markedprice, dwsp).doubleValue())), spotprice), capitalrate);
        return add;
    }

    /**
     * 资金费率 (F) = 溢价指数 (P) + clamp (利率 (I) - 溢价指数 (P), 0.05%, -0.05%)
     *
     * @param premiumIndex 溢价指数
     * @param interestRate 利率
     * @return 资金费率
     */
    public static BigDecimal capitalRate(BigDecimal premiumIndex, BigDecimal interestRate) {
        double v2 = sub(interestRate.doubleValue(), premiumIndex.doubleValue()).doubleValue();
        BigDecimal add = add(premiumIndex, ClampUtil.clamp(-0.05, v2, 0.05));
        return add;
    }

    /**
     * 资金费用基差率 = 资金费率* (至下一个缴付资金费用的时间 /资金费用的时间间隔 )
     *
     * @param capitalRate
     * @return 资金费率
     * @
     */
    public static BigDecimal brocc(BigDecimal capitalRate, ContractCoin contractCoin) {

        try {
            //当前时间
            Date currentDate = DateUtil.getCurrentDate();
            //凌晨4点
            Date beforedawn = DateUtil.datehhmmss(DateUtil.dateToStringDate(new Date()) + " 04:00:00");
            //中午12点
            Date noon = DateUtil.datehhmmss(DateUtil.dateToStringDate(new Date()) + " 12:00:00");

            //晚上8点
            Date night = DateUtil.datehhmmss(DateUtil.dateToStringDate(new Date()) + " 20:00:00");
            long nh = 1000 * 60 * 60;
            int clamp;
            if (beforedawn.getTime() <= currentDate.getTime() && currentDate.getTime() < noon.getTime()) {
                //取中午12点时间
                long ss = noon.getTime() - currentDate.getTime();
                long hour = ss / nh + 1L;
                int i = (int) hour;
                clamp = ClampUtil.clamp(1, i, 8);

            } else if (noon.getTime() <= currentDate.getTime() && currentDate.getTime() < night.getTime()) {
                //取晚上8点时间
                long ss = night.getTime() - currentDate.getTime();
                long hour = ss / nh + 1L;
                int i = (int) hour;
                clamp = ClampUtil.clamp(1, i, 8);

            } else {
                //取凌晨4点时间
                SimpleDateFormat df = new SimpleDateFormat("HH");
                String str = df.format(currentDate);
                int a = Integer.parseInt(str);
                Date datehh;
                if (a >= 0 && a < 4) {
                    datehh = DateUtil.datehhmmss(DateUtil.dateToStringDate(new Date()) + " 04:00:00");
                } else {
                    datehh = DateUtil.datehhmmss(DateUtil.getFetureDate(1) + " 04:00:00");
                }
                long ss = datehh.getTime() - currentDate.getTime();
                long hour = ss / nh + 1L;
                int i = (int) hour;
                clamp = ClampUtil.clamp(1, i, 8);
            }
            //时间间隔
            BigDecimal timeinterval = new BigDecimal("8");
            //至下一个缴付资金费用的时间
            BigDecimal paymenttime = new BigDecimal(clamp);
            //div(paymenttime,timeinterval,8);
            //资金费用基差率
            BigDecimal bigDecimal = mulRound(capitalRate, div(paymenttime, timeinterval, 8), 8);
            //BigDecimal brocc = contractCoin.getBaseSymbolScale().subtract(contractCoin.getCoinSymbolScale()).divide(new BigDecimal(3));
//            redisUtil.set(SysConstant.CONTRACT_CAPITAL_RATE + contractCoin.getSymbol(), bigDecimal);
            return bigDecimal;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 标记价格= 指数价格 * (1 + 资金费用基差率)
     *
     * @param indexPrice 指数价格
     * @param brocc      资金费用基差率
     * @return
     */
    public static BigDecimal markedprice(BigDecimal indexPrice, BigDecimal brocc) {
        BigDecimal bigDecimal = new BigDecimal("1");
        BigDecimal add = add(bigDecimal, brocc);
        BigDecimal mul = mul(indexPrice, add).setScale(8, BigDecimal.ROUND_DOWN);
        return mul;
    }

    /**
     * 仓位价值 = 合约数量 * 最小下单数量 * 标记价格
     *
     * @param noc         合约张数
     * @param markedprice 标记价格
     * @return
     */
    public static BigDecimal getlocationvalue(BigDecimal noc, BigDecimal markedprice) {
        // BigDecimal mul = mul(mul(noc, min), markedprice);
        return mul(noc, markedprice);
    }

    /**
     * @param noc 数量
     * @param min 最小下单量
     * @return
     */
    public static BigDecimal getnowp(BigDecimal noc, BigDecimal min) {
        return mul(noc, min);
    }

    /**
     * 资金费用 = 仓位价值 * 资金费率
     *
     * @param locationvalue 仓位价值
     * @param capitalRate   资金费率
     * @return
     */
    public static BigDecimal getCapitalcost(BigDecimal locationvalue, BigDecimal capitalRate) {
        return mul(locationvalue, capitalRate);
    }

    /**
     * 仓位保证金 = 下单数量 * 开仓价格 * 起始保证金
     *
     * @param amount 下单数量
     * @param price  开仓价格
     * @param start  起始保证金
     * @return
     */
    public static BigDecimal getbond(BigDecimal amount, BigDecimal price, BigDecimal start) {
        return mul(mul(amount, price), start);
    }

    /**
     * 持仓均价 = 总仓位价值 / 总开仓数量
     *
     * @param locationvalue 仓位价值
     * @param noc           手数
     * @return
     */
    public static BigDecimal getaverpop(BigDecimal locationvalue, BigDecimal noc) {
        return div(locationvalue, noc);
    }


    /**
     * 实际杠杆 = 总仓位价值 /(仓位价值/杠杆)
     *
     * @param locationvalue 总仓位价值
     * @param value         仓位价值/杠杆
     * @return
     */
    public static BigDecimal getlever(BigDecimal locationvalue, BigDecimal value) {
        return div(locationvalue, value);
    }


    /**
     * 多单  破产价格 = (下单数量 * 开仓价格 - 仓位保证金) / (下单数量 * (1+手续费率))
     * 空单  破产价格 = (下单数量 * 开仓价格 + 仓位保证金) / (下单数量 * (1+手续费率))
     *
     * @param amount      数量
     * @param price       开仓价格
     * @param marginTrade 仓位保证金
     * @param fee         手续费
     * @param sod         方向
     * @return
     */
    public static BigDecimal getbankruptcy(BigDecimal amount, BigDecimal price, BigDecimal marginTrade, BigDecimal fee, ContractOrderDirection sod) {
        BigDecimal bankruptcy = BigDecimal.ZERO;
        if (sod == ContractOrderDirection.BUY) {
            //下单数量 * 开仓价格
            BigDecimal mul = mul(amount, price);
            //(下单数量 * 开仓价格 - 仓位保证金)
            BigDecimal sub = sub(mul, marginTrade);
            //(1+手续费率)
            BigDecimal add = add(new BigDecimal("1"), fee);
            //(下单数量 * (1+手续费率))
            BigDecimal mul1 = mul(amount, add);
            bankruptcy = div(sub, mul1);

        } else {
            //下单数量 * 开仓价格
            BigDecimal mul = mul(amount, price);
            BigDecimal add = add(mul, marginTrade);
            //(1+手续费率)
            BigDecimal add1 = add(new BigDecimal("1"), fee);
            //(下单数量 * (1+手续费率))
            BigDecimal mul1 = mul(amount, add1);
            bankruptcy = div(add, mul1);
        }
        return bankruptcy;
    }

    /**
     * 起始保证金 = 下单数量 * 标记价格 * 起始保证金率
     *
     * @param amount       下单数量
     * @param contractCoin 交易对信息
     * @return
     */
    public static JSONObject getStart(BigDecimal amount, ContractCoin contractCoin, BigDecimal nowp, Long memberId, BigDecimal multiple) {
        Object markedPrice = getMarketprice().get(contractCoin.getSymbol());
        ContractBond bond = getBond(nowp, contractCoin, memberId, multiple);
        if (bond == null) {
            return null;
        }
        BigDecimal price = new BigDecimal(markedPrice.toString());
        BigDecimal start = mul(mul(amount, price), bond.getInitialMargin());
        String startRate = (bond.getInitialMargin().multiply(new BigDecimal(100))).toString() + "%";
        JSONObject map = new JSONObject();
        map.put("start", start.toString());
        map.put("startRate", startRate);
        return map;
    }

    /**
     * 维持保证金 = 下单数量 * 标记价格 * 维持保证金率
     *
     * @param amount       下单数量
     * @param contractCoin 交易对信息
     * @return
     */
    public static JSONObject getMaintain(BigDecimal amount, ContractCoin contractCoin, BigDecimal nowp, Long memberId, BigDecimal multiple) {
        Object markedPrice = getMarketprice().get(contractCoin.getSymbol());
        ContractBond bond = getBond(nowp, contractCoin, memberId, multiple);
        if (bond == null) {
            return null;
        }
        BigDecimal price = new BigDecimal(markedPrice.toString());

        BigDecimal maintain = mul(mul(amount, price), bond.getMaintain());
        String maintainRate = (bond.getMaintain().multiply(new BigDecimal(100))).toString() + "%";
        JSONObject map = new JSONObject();
        map.put("maintain", maintain.toString());
        map.put("maintainRate", maintainRate);
        return map;
    }

    /**
     * 根据下单数量 获取相应的 风险额度详情
     *
     * @param nowp         合约张数
     * @param contractCoin 交易对信息
     * @return
     */
    public static ContractBond getBond(BigDecimal nowp, ContractCoin contractCoin, Long memberId, BigDecimal multiple) {
        if (nowp.scale() != 0) {
            return null;
        }
        List<ContractBond> bonds;
        List<Object> list = redisUtil.lGet(SysConstant.CONTRACT_RISK_LIMIT + contractCoin.getSymbol(), 0, -1);
        if (list != null && list.size() > 0) {
            bonds = JSON.parseArray(JSON.toJSONString(list.get(0)), ContractBond.class);
        } else {
            Sort sort = new Sort(Sort.Direction.ASC, "multiple");
            bonds = bondService.findBySymbol(contractCoin.getSymbol(), sort);
            bonds.stream().sorted(Comparator.comparing(a -> Integer.parseInt(a.getMultiple()))).collect(Collectors.toList());
            redisUtil.lSet(SysConstant.CONTRACT_RISK_LIMIT + contractCoin.getSymbol(), bonds);
        }
        bonds.stream().sorted(Comparator.comparing(ContractBond::getPositionContractNum));

        if (memberId == 1L) {
            if (bonds.size() <= 0) {
                return null;
            }
            return bonds.get(bonds.size() - 1);
        }

        Integer currentNum = 0;

        Map map = getTotalNowpMap(memberId, contractCoin);
        for (Object key : map.keySet()) {
            if (ContractOrderStatus.COMPLETED.name().equalsIgnoreCase(key.toString())) {
                continue;
            }
            currentNum += Integer.parseInt(map.get(key).toString());
        }
        nowp = nowp.add(new BigDecimal(currentNum));
        BigDecimal positionValue = nowp.multiply(contractCoin.getMinVolume());
        if (nowp == null && multiple == null) {
            return bonds.get(bonds.size() - 1);
        }
        for (ContractBond bond : bonds) {
            Integer maxNum = bonds.stream().map(a -> Integer.parseInt(a.getPositionContractNum().toString())).max(Comparator.comparingInt(a -> a)).orElse(null);
            if (BigDecimalUtils.compare(new BigDecimal(bond.getPositionContractNum()), positionValue)
                    && !BigDecimalUtils.compare(new BigDecimal(bond.getPositionContractNum()), BigDecimal.valueOf(maxNum))
                    && multiple.compareTo(new BigDecimal(bond.getMultiple())) == 0) {
                return bond;
            }
        }
        return null;
    }
//    /**
//     * 根据根据用户id 获取指定币种的 持有及挂单中的 所有合约 交易数量总和
//     *
//     * @param memberId 用户id
//     * @param contractCoin 交易对信息
//     * @return
//     */
//    public static Integer getMaxPositionContractNum(Long memberId, ContractCoin contractCoin) {
//        int maxPositionContractNum=0;
//        //从缓存中获取 当前最大持有及挂单 仓位张数
//        Object correntContractNum=redisUtil.get(SysConstant.CONTRACT_POSITION_MAX_NUM + contractCoin.getSymbol()+memberId+"_:");
//        if(correntContractNum==null||Integer.parseInt(correntContractNum.toString())==0){
//            List<ContractPosition> list=positionService.findByMemberIdAndSymbol(memberId,contractCoin.getSymbol());
//            if(list==null||list.size()<=0){
//               return maxPositionContractNum;
//            }else{
//                for (ContractPosition position : list){
//                    maxPositionContractNum+=position.getContractAvailableNum();
//                }
//                List<Integer> statuss=new ArrayList<>();
//                statuss.add(ContractOrderStatus.TRADING.ordinal());
//                statuss.add(ContractOrderStatus.WAITING_TRIGGER.ordinal());
//                List<ContractOrder> list1=orderService.findByPositionFlagNotAndStatusInAndMemberId(ContractEnumType.ContractOrderPositionFlag.designate,statuss,memberId);
//                maxPositionContractNum += (list1.stream().mapToInt(a -> Integer.parseInt(a.getNowp().toString())).sum());
//
//                redisUtil.set(SysConstant.CONTRACT_POSITION_MAX_NUM + contractCoin.getSymbol()+memberId+"_:",maxPositionContractNum);
//            }
//        }
//       return maxPositionContractNum;
//    }

    /**
     * 根据根据用户id 获取指定币种的 持有及挂单中的 所有合约 交易数量总和
     *
     * @param memberId     用户id
     * @param contractCoin 交易对信息
     * @return
     */
    public static BigDecimal getTotalBond(Long memberId, ContractCoin contractCoin) {
        BigDecimal positionValue = BigDecimal.ZERO;
        Integer currentNum = 0;
        Map map = getTotalNowpMap(memberId, contractCoin);
        for (Object key : map.keySet()) {
            if (ContractOrderStatus.COMPLETED.name().equalsIgnoreCase(key.toString())) {
                continue;
            }
            currentNum += Integer.parseInt(map.get(key).toString());
        }
//        nowp=nowp.add(new BigDecimal(currentNum));
        return positionValue;
    }

    /**
     * 根据根据用户id 币种 交易状态 交易类型(开多,开空) 获取 总手数
     *
     * @param memberId     用户id
     * @param contractCoin 交易对信息
     * @return
     */
    public static Map<?, ?> getTotalNowpMap(Long memberId, ContractCoin contractCoin) {
        String key = SysConstant.CONTRACT_MAX_NOWP_MAP + contractCoin.getSymbol() + "_" + memberId + "_:";
        //获取缓存
        Map map = redisUtil.hmget(key);
        //查询数据库
        if (map == null || map.keySet().size() <= 0) {
            List<ContractOrder> orderList = orderService.findAllByMemberId(memberId, contractCoin.getSymbol());
            //未完成状态
            //循环输出 值
            for (ContractOrderStatus status : ContractOrderStatus.values()) {
                Integer totalNowp = orderList.stream().filter(a -> a.getStatus() == status &&
                        a.getStatus() != ContractOrderStatus.CANCELED &&
                        a.getStatus() != ContractOrderStatus.OVERTIMED)
                        .mapToInt(a -> a.getNowp().intValue()).sum();
                map.put(status.name(), totalNowp);
            }
            //持有仓位根据 开多 开空 存储可用数量
            List<ContractPosition> list = positionService.findByMemberIdAndSymbol(memberId, contractCoin.getSymbol());
            for (ContractPosition position : list) {
                Integer totalNowp = position.getContractTotalNum();
                map.put(position.getPositionType().name(), totalNowp);
            }
            redisUtil.hmset(key, map);
        }
        return map;
    }

    /**
     * 判断 余额是否 足够支付保证金
     *
     * @param memberId     用户id
     * @param contractCoin 交易对信息
     * @return
     */
    public static Boolean isBalanceEnoughBond(Long memberId, ContractCoin contractCoin, BigDecimal nowp, BigDecimal multiple) {
        ContractWallet wallet = walletService.findByMemberId(memberId);
        BigDecimal currentNum = BigDecimal.ZERO;
        Map map = getTotalNowpMap(memberId, contractCoin);
        for (Object key : map.keySet()) {
            if (ContractOrderStatus.COMPLETED.name().equalsIgnoreCase(key.toString())) {
                continue;
            }
            currentNum = currentNum.add(new BigDecimal(map.get(key).toString()));
        }
        if (nowp != null && !BigDecimalUtils.compare(BigDecimal.ZERO, nowp)) {
            currentNum = nowp.add(currentNum);
        }
        BigDecimal marketPrice = getMarketprice().get(contractCoin.getSymbol());

        BigDecimal positionValue = currentNum.multiply(contractCoin.getMinVolume()).multiply(marketPrice);
        ContractBond bond = getBond(nowp, contractCoin, memberId, multiple);
        return BigDecimalUtils.compare(wallet.getBalance(), positionValue.multiply(bond.getInitialMargin()));
    }


    /**
     * 判断 余额是否 足够支付保证金
     *
     * @param memberId  用户id
     * @param symbol    交易对信息
     * @param symbol    交易对信息
     * @param subStatus 需要做减法的缓存项
     * @param addStatus 需要做加法的缓存项
     * @param nowp      变更的数量
     * @return
     */
    public static Boolean updateContractMaxNowpMap(Long memberId, String symbol, ContractOrderStatus subStatus, ContractOrderStatus addStatus, BigDecimal nowp) {
        String key = SysConstant.CONTRACT_MAX_NOWP_MAP + symbol + "_" + memberId + "_:";
        if (addStatus != null) {
            Object addNowp = redisUtil.hget(key, addStatus.name());
            if (addNowp == null) {
                redisUtil.hset(key, addStatus.name(), nowp);
            } else {
                redisUtil.hset(key, addStatus.name(), new BigDecimal(addNowp.toString()).add(nowp));
            }
        }
        if (subStatus != null) {
            Object subNowp = redisUtil.hget(key, subStatus.name());
            if (subNowp != null && !BigDecimalUtils.compare(BigDecimal.ZERO, new BigDecimal(subNowp.toString()))) {
                redisUtil.hset(key, subStatus.name(), new BigDecimal(subNowp.toString()).subtract(nowp));
            }
        }
        return true;
    }

    /**
     * 实时获取风险额度列表;
     */
    public static List<ContractBond> getTimelyBonds(Long memberId, String symbol, BigDecimal nowp) {
        List<ContractBond> bonds;
        List<Object> list = redisUtil.lGet(SysConstant.CONTRACT_RISK_LIMIT + symbol, 0, -1);
        if (list != null && list.size() > 0) {
            bonds = JSON.parseArray(JSON.toJSONString(list.get(0)), ContractBond.class);
        } else {
            Sort sort = new Sort(Sort.Direction.ASC, "multiple");
            bonds = bondService.findBySymbol(symbol, sort);
            redisUtil.lSet(SysConstant.CONTRACT_RISK_LIMIT + symbol, bonds);
        }
        bonds = bonds.stream().sorted(Comparator.comparing(a -> a.getPositionContractNum().intValue())).collect(Collectors.toList());
        List<ContractCoin> baseSymbols = findBaseSymbols();
        baseSymbols = baseSymbols.stream().filter(a -> symbol.equalsIgnoreCase(a.getSymbol())).collect(Collectors.toList());
        ContractCoin contractCoin = baseSymbols.get(0);
        Integer currentNum = 0;
        Map map = getTotalNowpMap(memberId, contractCoin);
        for (Object key : map.keySet()) {
            if (ContractOrderStatus.COMPLETED.name().equalsIgnoreCase(key.toString())) {
                continue;
            }
            currentNum += Integer.parseInt(map.get(key).toString());
        }
        nowp = nowp.add(new BigDecimal(currentNum));
        BigDecimal positionValue = nowp.multiply(contractCoin.getMinVolume());

        Integer maxNum = bonds.stream().map(a -> Integer.parseInt(a.getPositionContractNum().toString())).max(Comparator.comparingInt(a -> a)).orElse(null);
        bonds = bonds.stream().filter(
                bond -> BigDecimalUtils.compare(new BigDecimal(bond.getPositionContractNum()), positionValue)
                        && !BigDecimalUtils.compare(new BigDecimal(bond.getPositionContractNum()), BigDecimal.valueOf(maxNum)))
                .sorted(Comparator.comparing(a -> Integer.parseInt(a.getMultiple())))
                .collect(Collectors.toList());
        return bonds;
    }

    /**
     * 获取交易对详情列表
     */
    public static List<ContractCoin> findBaseSymbols() {
        List<ContractCoin> baseSymbols;
        List<Object> symbolCoins = redisUtil.lGet(SysConstant.CONTRACT_SYMBOL_COIN, 0, -1);
        if (symbolCoins == null || symbolCoins.size() <= 0) {
            baseSymbols = contractCoinService.findAllEnabled();
            redisUtil.lSet(SysConstant.CONTRACT_SYMBOL_COIN, baseSymbols);
        } else {
            baseSymbols = JSON.parseArray(JSON.toJSONString(symbolCoins.get(0)), ContractCoin.class);
        }
        return baseSymbols;
    }

    /**
     * 获取交易对详情
     */
    public static ContractCoin getBaseSymbol(String symbol) {
        List<ContractCoin> baseSymbols = findBaseSymbols();
        baseSymbols = baseSymbols.stream().filter(a -> a.getSymbol().equalsIgnoreCase(symbol)).collect(Collectors.toList());
        if (baseSymbols == null || baseSymbols.size() <= 0) {
            baseSymbols = contractCoinService.findAllEnabled();
            redisUtil.lSet(SysConstant.CONTRACT_SYMBOL_COIN, baseSymbols);
        }
        return baseSymbols.get(0);
    }

    /**
     * 所有交易对指数价格
     * 每5秒计算一次指数价格
     */
    public static Map<String, BigDecimal> setIndexprice() {
        log.info("计算指数价格任务启动.......");
        Map map = DataUtil.getIndexPrice();
        if (map == null || map.isEmpty()) {
            map = redisUtil.hmget(SysConstant.CONTRACT_INDEX_PRICE);
            if (!redisUtil.hasKey(SysConstant.CONTRACT_INDEX_PRICE) || map == null) {
                map = DataUtil.getIndexPrice();
            }
        }
//        log.info("定时任务指数价格 : "+JSON.toJSONString(map));
        redisUtil.hmset(SysConstant.CONTRACT_INDEX_PRICE, map);
        return map;
    }

    /**
     * 定时器获取 指标价格 推送到前台;
     *
     * @return
     */
    public static Map<String, BigDecimal> getIndexprice() {
        Map map = redisUtil.hmget(SysConstant.CONTRACT_INDEX_PRICE);
        if (!redisUtil.hasKey(SysConstant.CONTRACT_INDEX_PRICE) || map == null) {
            map = DataUtil.getIndexPrice();
            redisUtil.hmset(SysConstant.CONTRACT_INDEX_PRICE, map);
        }
        return map;
    }

    /**
     * 所有合约利率
     * 每1分钟计算一次合约利率
     */
    public static Map<String, BigDecimal> setInterestRate() {
        log.info("计算合约利率任务启动.......");
        List<ContractCoin> contractCoinList = ContractUtils.findBaseSymbols();
        Map map = new HashMap<>();
        contractCoinList.stream().forEach(contractCoin -> {
            BigDecimal bigDecimal = ContractUtils.interestRate(contractCoin.getCoinSymbolScale().toString(), contractCoin.getBaseSymbolScale().toString());
            map.put(contractCoin.getSymbol(), bigDecimal);
        });
        log.info("定时任务合约利率 : "+JSON.toJSONString(map));
        redisUtil.hmset(SysConstant.CONTRACT_INTERES_TRATE, map);
        return map;
    }

    /**
     * 所有合约利率
     * 每1分钟计算一次合约利率
     */
    public static Map<String, BigDecimal> getInterestRate() {
        Map map = redisUtil.hmget(SysConstant.CONTRACT_INTERES_TRATE);
        if (map == null || map.isEmpty()) {
            map = setInterestRate();
            redisUtil.hmset(SysConstant.CONTRACT_INTERES_TRATE, map);
        }
        return map;
    }

    /**
     * 溢价指数 (P) = ( Max ( 0 , 深度加权买价 - 标记价格) - Max ( 0 , 标记价格 - 深度加权卖价)) / 现货价格 + 当前资金费率
     * 每分钟溢价指数
     */
    public static Map<String, JSONObject> setPremiumIndex() {
        log.info("开始计算每分钟的溢价指数......");
        Map map = new HashMap<>();
        List<ContractCoin> contractCoinList = ContractUtils.findBaseSymbols();
        Map<String, BigDecimal> indexMap = getIndexprice();
        Map<String, BigDecimal> zijinfeilvMap = getCapitalRate();
        Map<String, BigDecimal> heyuelilvMap = getInterestRate();
        Map<String, BigDecimal> marketMap = getMarketprice();
        Map<String, JSONObject> yijiazhishuMap = getPremiumIndex();

        contractCoinList.stream().forEach(contractCoin -> {
            //现货价格
            BigDecimal indexprice = indexMap.get(contractCoin.getSymbol());
            BigDecimal capital_rate;
            if (zijinfeilvMap!=null&&zijinfeilvMap.containsKey(contractCoin.getSymbol()) && zijinfeilvMap.get(contractCoin.getSymbol()) != null) {
                capital_rate = zijinfeilvMap.get(contractCoin.getSymbol());
            } else {
                //不存在资金费率取合约利率
                capital_rate = heyuelilvMap.get(contractCoin.getSymbol());
                if (capital_rate == null) {
                    capital_rate = getInterestRate().get(contractCoin.getSymbol());
                }
            }
            //获得标记价格 markedPrice  判断标记价格是否存在
            BigDecimal markedPrice = marketMap.get(contractCoin.getSymbol());
            //获取币种的买卖订单
            List<ContractOrder> contractOrders = orderService.findByDirection(contractCoin.getSymbol());
            //初始化 买数量
            BigDecimal num1 = BigDecimal.ZERO.setScale(8);
            //初始化 买价格
            BigDecimal price1 = BigDecimal.ZERO;
            //初始化 卖数量
            BigDecimal num2 = BigDecimal.ZERO.setScale(8);
            //初始化 卖价格
            BigDecimal price2 = BigDecimal.ZERO;
            for (ContractOrder contractOrder : contractOrders) {
                if (contractOrder.getDirection().equals("BUY")) {
                    //买订单
                    if (!BigDecimalUtils.compare(price1, new BigDecimal("100000.00000000"))) {
                        num1 = BigDecimalUtils.add(num1, contractOrder.getAmount());
                        price1 = BigDecimalUtils.add(price1, BigDecimalUtils.mul(contractOrder.getAmount(), contractOrder.getPrice()));
                    } else {
                        continue;
                    }
                } else {
                    //卖订单
                    if (!BigDecimalUtils.compare(price2, new BigDecimal("100000.00000000"))) {
                        num2 = BigDecimalUtils.add(num2, contractOrder.getAmount());
                        price2 = BigDecimalUtils.add(price2, BigDecimalUtils.mul(contractOrder.getAmount(), contractOrder.getPrice()));
                    } else {
                        continue;
                    }
                }
            }
            BigDecimal dwpp = BigDecimal.ZERO;
            BigDecimal dwsp = BigDecimal.ZERO;
            //深度加权买价
            num1.setScale(8);
            num2.setScale(8);
            if (num1.compareTo(BigDecimal.ZERO)!=0 && price1.compareTo(BigDecimal.ZERO)!=0) {
                dwpp = div(price1, num1);
            }
            //深度加权卖价
            if (num2.compareTo(BigDecimal.ZERO)!=0 && price2.compareTo(BigDecimal.ZERO)!=0) {
                dwsp = div(price2, num2);
            }
            //溢价指数
            BigDecimal premiumIndex = ContractUtils.premiumIndex(dwpp, dwsp, markedPrice, indexprice, capital_rate);
            JSONObject json = new JSONObject();
            if (yijiazhishuMap!=null&&yijiazhishuMap.containsKey(contractCoin.getSymbol()) && yijiazhishuMap.get(contractCoin.getSymbol()) != null) {
                JSONObject jsonObject = yijiazhishuMap.get(contractCoin.getSymbol());
                Integer num;
                num = jsonObject.getInteger("num");
                BigDecimal interest = jsonObject.getBigDecimal("premiumIndex");
                num++;
                BigDecimal add = BigDecimalUtils.add(interest, premiumIndex);
                json.put("num", num);
                json.put("premiumIndex", add);
                map.put(contractCoin.getSymbol(), json);
            } else {
                json.put("num", 1);
                json.put("premiumIndex", premiumIndex);
                //CONTRACT_INTERES_TRATE
                map.put(contractCoin.getSymbol(), json);
            }
        });
        log.info("定时任务溢价指数 : "+JSON.toJSONString(map));
        redisUtil.hmset(SysConstant.CONTRACT_PREMIUM_INDEX, map);
        return map;
    }

    /**
     * 溢价指数 (P) = ( Max ( 0 , 深度加权买价 - 标记价格) - Max ( 0 , 标记价格 - 深度加权卖价)) / 现货价格 + 当前资金费率
     * 每分钟溢价指数
     */
    public static Map<String, JSONObject> getPremiumIndex() {
        Map map = redisUtil.hmget(SysConstant.CONTRACT_PREMIUM_INDEX);
//        if (map == null || map.isEmpty()) {
//            map = setPremiumIndex();
//            redisUtil.hmset(SysConstant.CONTRACT_PREMIUM_INDEX,map);
//        }
        return map;
    }

    /**
     * 计算资金费率
     * 每天的上午4 中午12点 晚上8点
     */
    public static Map<String, BigDecimal> setCapitalRate() {
        List<ContractCoin> contractCoinList = ContractUtils.findBaseSymbols();
        Map map = new HashMap<>();
        Map<String, BigDecimal> heyuelilvMap = getInterestRate();
        contractCoinList.stream().forEach(contractCoin -> {
            //获取合约的溢价指数
            JSONObject jsonpi;
            jsonpi = getPremiumIndex().get(contractCoin.getSymbol());
            BigDecimal premiumIndex = jsonpi.getBigDecimal("premiumIndex");
            //合约的总时间(按分钟)
            BigDecimal num = jsonpi.getBigDecimal("num");
            //加权的溢价指数
            BigDecimal divpremiumIndex = div(premiumIndex, num, 8);
            //从redis获取合约的利率
            BigDecimal it = heyuelilvMap.get(contractCoin.getSymbol());
            //计算溢价指数的加权值
            BigDecimal bigDecimal = ContractUtils.capitalRate(divpremiumIndex, it);
            //每8小时计算的资金费率存入redis
            map.put(contractCoin.getSymbol(), bigDecimal);
        });
        redisUtil.hmset(SysConstant.CONTRACT_CAPITAL_RATE, map);
        log.info("定时任务资金费率 : "+JSON.toJSONString(map));
        return map;
    }

    /**
     * 计算资金费率
     * 每天的上午4 中午12点 晚上8点
     */
    public static Map<String, BigDecimal> getCapitalRate() {
        Map map = redisUtil.hmget(SysConstant.CONTRACT_CAPITAL_RATE);
//        if (map == null || map.isEmpty()) {
//            map = setCapitalRate();
//            redisUtil.hmset(SysConstant.CONTRACT_CAPITAL_RATE,map);
//        }
        return map;
    }

    /**
     * 计算资金费用基差率
     * 资金费用基差率 = 资金费率* (至下一个缴付资金费用的时间/资金费用的时间间隔)
     */
    public static Map<String, BigDecimal> setbasisrate() {
        log.info("计算资金费用基差率任务启动.......");
        List<ContractCoin> contractCoinList = ContractUtils.findBaseSymbols();
        Map<String, BigDecimal> zijinfeilvMap = getCapitalRate();
        Map<String, BigDecimal> heyuelilvMap = getInterestRate();
        Map map = new HashMap<>();
        contractCoinList.stream().forEach(contractCoin -> {
            BigDecimal capitalRate;
            if (zijinfeilvMap!=null&&zijinfeilvMap.containsKey(contractCoin.getSymbol()) && zijinfeilvMap.get(contractCoin.getSymbol()) != null) {
                capitalRate = zijinfeilvMap.get(contractCoin.getSymbol());
            } else {
                //不存在资金费率取合约利率
                capitalRate = heyuelilvMap.get(contractCoin.getSymbol());
            }
            BigDecimal jichalv = brocc(capitalRate, contractCoin);
            map.put(contractCoin.getSymbol(), jichalv);
        });
        redisUtil.hmset(SysConstant.CONTRACT_BASE_RATE_OF_CAPITAL_COST, map);
        log.info("定时任务资金费用基差率 : "+JSON.toJSONString(map));
        return map;
    }

    /**
     * 计算资金费用基差率
     * 资金费用基差率 = 资金费率* (至下一个缴付资金费用的时间/资金费用的时间间隔)
     */
    public static Map<String, BigDecimal> getbasisrate() {
        Map map = redisUtil.hmget(SysConstant.CONTRACT_BASE_RATE_OF_CAPITAL_COST);
        if (map == null || map.isEmpty()) {
            map = setbasisrate();
            redisUtil.hmset(SysConstant.CONTRACT_BASE_RATE_OF_CAPITAL_COST,map);
        }
        return map;
    }

    /**
     * 计算标记价格
     * 标记价格= 指数价格 * (1 + 资金费用基差率)
     */
    public static Map<String, BigDecimal> setMarketprice() {
        log.info("计算标记价格任务启动.......");
        Map map = new HashMap<>();
        List<ContractCoin> contractCoinList = ContractUtils.findBaseSymbols();
        Map<String, BigDecimal> indexMap = getIndexprice();
        Map<String, BigDecimal> jichalvMap = getbasisrate();
        contractCoinList.stream().forEach(contractCoin -> {
            //判断指数价格是否存在
            BigDecimal indexPrice, jichalv;
            indexPrice = indexMap.get(contractCoin.getSymbol());
            jichalv = jichalvMap.get(contractCoin.getSymbol());
            //费用基差率结束,计算标记价格
            log.info("indexPrice----------------:" + indexPrice);
            log.info("jichalv----------------:" + jichalv);
            BigDecimal markedprice = ContractUtils.markedprice(indexPrice, jichalv);
            map.put(contractCoin.getSymbol(), markedprice);
        });
        redisUtil.hmset(SysConstant.CONTRACT_MARKED_PRICE, map);
        return map;
    }

    /**
     * 计算标记价格
     * 标记价格= 指数价格 * (1 + 资金费用基差率)
     */
    public static Map<String, BigDecimal> getMarketprice() {
        Map map = redisUtil.hmget(SysConstant.CONTRACT_MARKED_PRICE);
        if (map == null || map.isEmpty()) {
            map = setMarketprice();
            redisUtil.hmset(SysConstant.CONTRACT_MARKED_PRICE,map);
        }
        return map;
    }
}
